home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
tcl
/
tclm_1_0.lha
/
tclm-1.0
/
MLIB
/
mfileutil.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-08-16
|
21KB
|
925 lines
/*-
* Copyright (c) 1993 Michael B. Durian. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Michael B. Durian.
* 4. The name of the the Author may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* mfileutil.c,v 1.13 1993/05/07 17:45:18 durian Exp
*/
static char cvsid[] = "mfileutil.c,v 1.13 1993/05/07 17:45:18 durian Exp";
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/fcntl.h>
#include "mutil.h"
/* hack so we know if a read failed because of an eof instead of bad data */
int MidiEof = 0;
char MidiError[256];
int
read_header_chunk(mfile, hchunk)
int mfile;
HCHUNK *hchunk;
{
if (mread(mfile, hchunk->str, sizeof(hchunk->str)) !=
sizeof(hchunk->str)) {
if (!MidiEof)
sprintf(MidiError,
"Couldn't read header chunk identifier");
return (0);
}
if (mread(mfile, (char *)&hchunk->length, sizeof(hchunk->length)) !=
sizeof(hchunk->length)) {
sprintf(MidiError, "Couldn't read header chunk length");
return (0);
}
if (mread(mfile, (char *)&hchunk->format, sizeof(hchunk->format)) !=
sizeof(hchunk->format)) {
sprintf(MidiError, "Couldn't read header chunk format");
return (0);
}
if (mread(mfile, (char *)&hchunk->num_trks, sizeof(hchunk->num_trks))
!= sizeof(hchunk->num_trks)) {
sprintf(MidiError, "Couldn't read header chunk num_trks");
return (0);
}
if (mread(mfile, (char *)&hchunk->division, sizeof(hchunk->division))
!= sizeof(hchunk->division)) {
sprintf(MidiError, "Couldn't read header chunk division");
return (0);
}
/* fix byte ordering */
hchunk->length = mtohl(hchunk->length);
hchunk->format = mtohs(hchunk->format);
hchunk->num_trks = mtohs(hchunk->num_trks);
hchunk->division = mtohs(hchunk->division);
if (strncmp(hchunk->str, "MThd", 4) != 0) {
sprintf(MidiError, "Not a standard MIDI file");
return (0);
}
if (hchunk->length != 6) {
sprintf(MidiError, "Bad header chunk size");
return (0);
}
if (hchunk->format == 0 && hchunk->num_trks != 1) {
sprintf(MidiError,
"Midi format 0, but number of tracks (%d) is > 1",
hchunk->num_trks);
return (0);
}
return (1);
}
int
read_track_chunk(mfile, tracks)
int mfile;
TCHUNK *tracks;
{
if (mread(mfile, tracks->str, 4) != 4) {
sprintf(MidiError, "Couldn't read track chunk identifier");
return (0);
}
if (strncmp(tracks->str, "MTrk", 4) != 0) {
sprintf(MidiError, "Bad track chunk identifier");
return (0);
}
if (mread(mfile, (char *)&tracks->length, sizeof(tracks->length)) !=
sizeof(tracks->length)) {
sprintf(MidiError, "Couldn't read track length");
return (0);
}
tracks->msize = tracks->length = mtohl(tracks->length);
tracks->pos = 0;
/* allocate space for tracks events */
if ((tracks->events = (unsigned char *) malloc(tracks->length)) ==
NULL) {
sprintf(MidiError, "Not enough memory for track data");
return (0);
}
tracks->event_start = tracks->events;
if (mread(mfile, (char *)tracks->events, tracks->length) !=
tracks->length) {
sprintf(MidiError, "Couldn't read track data");
return (0);
}
tracks->events = tracks->event_start;
return (1);
}
int
skip_track_chunk(mfile)
int mfile;
{
long length;
char str[4];
if (mread(mfile, str, sizeof(str)) != sizeof(str)) {
sprintf(MidiError, "Couldn't read track chunk identifier");
return (0);
}
if (strncmp(str, "MTrk", 4) != 0) {
sprintf(MidiError, "Bad track chunk identifier");
return (0);
}
if (mread(mfile, (char *)&length, sizeof(length)) != sizeof(length)) {
sprintf(MidiError, "Couldn't read track length");
return (0);
}
length = mtohl(length);
if (lseek(mfile, length, SEEK_CUR) == -1) {
sprintf(MidiError, "Couldn't seek past track");
return (0);
}
return (1);
}
int
split_type_zero(tracks)
TCHUNK *tracks;
{
long tempo_delta;
long data_delta;
long timing;
EVENT_TYPE event_type;
int data_size;
int event_size;
int i;
int offset;
int malloced_data;
int malloced_tempo;
int malloc_inc;
int tempo_size;
int time_len;
unsigned char event[256];
unsigned char *data_events;
unsigned char *data_ptr;
unsigned char *last_events;
unsigned char *tempo_events;
unsigned char *tempo_ptr;
unsigned char var_time[4];
data_size = 0;
tempo_size = 0;
tempo_delta = 0;
data_delta = 0;
malloc_inc = 512;
if ((data_events = (unsigned char *) malloc(malloc_inc))
== NULL) {
sprintf(MidiError, "Not enought memory to split track");
return (0);
}
malloced_data = malloc_inc;
if ((tempo_events = (unsigned char *) malloc(malloc_inc))
== NULL) {
sprintf(MidiError, "Not enought memory to split track");
return (0);
}
malloced_tempo = malloc_inc;
data_ptr = data_events;
tempo_ptr = tempo_events;
while (event_size = get_smf_event(&tracks[0], event, &event_type)) {
if (event_size == -1) {
sprintf(MidiError,
"Problem getting event while splitting");
return (0);
}
if (event_type == NORMAL || event_type == SYSEX) {
timing = var2fix(event, &offset);
data_delta += timing;
time_len = fix2var(data_delta, var_time);
data_size += event_size - offset + time_len;
if (data_size > malloced_data) {
last_events = data_events;
if (!more_memory((char **)&data_events,
malloced_data + malloc_inc)) {
sprintf(MidiError,
"Not enough memory");
return (0);
}
if (data_events != last_events)
data_ptr = data_events + (data_ptr -
last_events);
}
/* copy in timing */
for (i = 0; i < time_len; i++)
*data_ptr++ = var_time[i];
/* copy in data */
for (i = offset; i < event_size; i++)
*data_ptr++ = event[i];
/* add timing to tempo delta */
tempo_delta += timing;
data_delta = 0;
} else {
timing = var2fix(event, &offset);
tempo_delta += timing;
time_len = fix2var(tempo_delta, var_time);
tempo_size += event_size - offset + time_len;
if (tempo_size > malloced_tempo) {
last_events = tempo_events;
if (!more_memory((char **)&tempo_events,
malloced_tempo + malloc_inc)) {
sprintf(MidiError,
"Not enough memory");
return (0);
}
if (tempo_events != last_events)
tempo_ptr = data_events + (tempo_ptr -
last_events);
}
/* copy in timing */
for (i = 0; i < time_len; i++)
*tempo_ptr++ = var_time[i];
/* copy in data */
for (i = offset; i < event_size; i++)
*tempo_ptr++ = event[i];
/* add timing to tempo delta */
data_delta += timing;
tempo_delta = 0;
}
}
/* add eot to tempo track */
event[0] = 0xff;
event[1] = METAEOT;
event[2] = 0;
event_size = 3;
time_len = fix2var(tempo_delta, var_time);
tempo_size += event_size + time_len;
if (tempo_size > malloced_tempo) {
last_events = tempo_events;
if (!more_memory((char **)&tempo_events, malloced_tempo +
malloc_inc)) {
sprintf(MidiError, "Not enough memory");
return (0);
}
if (tempo_events != last_events)
tempo_ptr = data_events + (tempo_ptr - last_events);
}
/* copy in timing */
for (i = 0; i < time_len; i++)
*tempo_ptr++ = var_time[i];
/* copy in data */
for (i = 0; i < event_size; i++)
*tempo_ptr++ = event[i];
free(tracks[0].events);
tracks[0].events = tracks[0].event_start = tempo_events;
tracks[1].events = tracks[1].event_start = data_events;
tracks[0].msize = malloced_tempo;
tracks[1].msize = malloced_data;
tracks[0].length = tempo_size;
tracks[1].length = data_size;
tracks[0].pos = 0;
tracks[1].pos = 0;
(void) strncpy(tracks[0].str, "MTrk", 4);
(void) strncpy(tracks[1].str, "MTrk", 4);
return (1);
}
int
more_memory(ptr, size)
char **ptr;
int size;
{
if ((*ptr = realloc(*ptr, size)) == NULL)
return (0);
else
return (1);
}
int
get_smf_event(track, event, event_type)
TCHUNK *track;
unsigned char *event;
EVENT_TYPE *event_type;
{
long length = 0;
int i;
int size;
static int extra_bytes[] = {2, 2, 2, 2, 1, 1, 2, 0};
unsigned char etype;
if (track->pos >= track->length)
return (0);
/*
* get timing bytes
*/
if (track->pos++ == track->length)
return (-1);
*event++ = *track->events++;
size = 1;
while (*(event - 1) & 0x80) {
if (track->pos++ == track->length)
return (-1);
*event++ = *track->events++;
size++;
}
if (track->pos++ == track->length)
return (-1);
*event = *track->events++;
/* get event type */
switch (*event) {
case 0xff:
/* meta event */
if (track->pos++ == track->length)
return (-1);
size++;
event++;
*event_type = *event++ = *track->events++;
size++;
/* get length as a variable length */
do {
if (track->pos++ == track->length)
return (-1);
length = (length << 7) + (*track->events & 0x7f);
*event = *track->events++;
size++;
} while (*event++ & 0x80);
for (; length > 0; length--) {
/* get event */
if (track->pos++ == track->length)
return (-1);
*event++ = *track->events++;
size++;
}
track->read_rs = 0;
break;
case 0xf0:
case 0xf7:
event++;
size++;
*event_type = SYSEX;
/* get length as variable length value */
do {
if (track->pos++ == track->length)
return (-1);
length = (length << 7) + (*track->events & 0x7f);
*event = *track->events++;
size++;
} while (*event++ & 0x80);
for (; length > 0; length--) {
/* get event */
if (track->pos++ == track->length)
return (-1);
*event++ = *track->events++;
size++;
}
track->read_rs = 0;
break;
default:
*event_type = NORMAL;
size++;
if (*event & 0x80) {
etype = *event;
track->read_rs = etype;
for (i = 0; i < extra_bytes[(etype >> 4) & 0x07];
i++) {
event++;
if (track->pos++ == track->length)
return(-1);
*event = *track->events++;
size++;
}
} else {
/* it is using running state */
/* get other data bytes */
switch ((track->read_rs >> 4) & 0x0f) {
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0e:
event++;
if (track->pos++ == track->length)
return (-1);
*event = *track->events++;
size++;
break;
default:
break;
}
}
}
return (size);
}
int
put_smf_event(track, event, event_size)
TCHUNK *track;
unsigned char *event;
int event_size;
{
unsigned char *event_cpy;
unsigned char *event_ptr;
int i;
int pos;
/* make a copy of the event we can modify */
if ((event_cpy = (unsigned char *)malloc(event_size)) == NULL) {
sprintf(MidiError, "Out of memory in put_smf_event");
return (0);
}
for (i = 0; i < event_size; i++)
event_cpy[i] = event[i];
/* skip timing part */
(void)var2fix(event_cpy, &pos);
event_ptr = event_cpy + pos;
/*
* check event type to see if we can dump the running state
* If there is no running state, assume whoever sent us
* the data got it right.
*/
if (*event_ptr & 0x80) {
switch (*event_ptr & 0xf0) {
case 0xf0:
/* clear running state for these */
track->write_rs = 0;
break;
default:
if (*event_ptr != track->write_rs)
track->write_rs = *event_ptr;
else {
/* it's the same as the last, so dump it */
event_size--;
while (pos++ < event_size) {
*event_ptr = *(event_ptr + 1);
event_ptr++;
}
}
}
}
if (track->msize == 0) {
if ((track->event_start = (unsigned char *)malloc(
BUFSIZ)) == NULL) {
sprintf(MidiError, "Not enough memory for new event");
return (0);
}
track->msize = BUFSIZ;
track->events = track->event_start;
}
track->length += event_size;
if (track->length > track->msize) {
track->msize += BUFSIZ;
if ((track->event_start =
(unsigned char *)realloc(track->event_start, track->msize))
== NULL) {
perror("");
sprintf(MidiError, "Not enough memory for new event");
track->length -= event_size;
track->msize -= BUFSIZ;
return (0);
}
/* starting position might move */
track->events = track->event_start + track->pos;
}
memcpy(track->event_start + track->length - event_size, event_cpy,
event_size);
free(event_cpy);
return (1);
}
void
rewind_track(track)
TCHUNK *track;
{
track->pos = 0;
track->events = track->event_start;
track->read_rs = 0;
}
void
reset_track(track)
TCHUNK *track;
{
track->pos = 0;
track->length = 0;
track->events = track->event_start;
track->read_rs = 0;
track->write_rs = 0;
}
int
write_header_chunk(mfile, hchunk)
int mfile;
HCHUNK *hchunk;
{
(void)strncpy(hchunk->str, "MThd", 4);
if (mwrite(mfile, hchunk->str, 4) != 4) {
sprintf(MidiError, "Couldn't write header chunk identifier");
return (0);
}
hchunk->length = 6;
hchunk->length = htoml(hchunk->length);
if (mwrite(mfile, (char *)&hchunk->length, sizeof(hchunk->length)) !=
sizeof(hchunk->length)) {
sprintf(MidiError, "Couldn't write header chunk length");
return (0);
}
hchunk->length = mtohl(hchunk->length);
hchunk->format = htoms(hchunk->format);
if (mwrite(mfile, (char *)&hchunk->format, sizeof(hchunk->format)) !=
sizeof(hchunk->format)) {
sprintf(MidiError, "Couldn't write header chunk format");
return (0);
}
hchunk->format = mtohs(hchunk->format);
hchunk->num_trks = htoms(hchunk->num_trks);
if (mwrite(mfile, (char *)&hchunk->num_trks, sizeof(hchunk->num_trks))
!= sizeof(hchunk->num_trks)) {
sprintf(MidiError, "Couldn't write number of tracks");
return (0);
}
hchunk->num_trks = mtohs(hchunk->num_trks);
hchunk->division = htoms(hchunk->division);
if (mwrite(mfile, (char *)&hchunk->division, sizeof(hchunk->division))
!= sizeof(hchunk->division)) {
sprintf(MidiError, "Couldn't write header chunk division");
return (0);
}
hchunk->division = mtohs(hchunk->division);
return (1);
}
int
write_track_chunk(mfile, tchunk)
int mfile;
TCHUNK *tchunk;
{
long midi_length;
(void)strncpy(tchunk->str, "MTrk", 4);
if (mwrite(mfile, tchunk->str, 4) != 4) {
sprintf(MidiError, "Couldn't write track chunk identifier");
return (0);
}
midi_length = htoml(tchunk->length);
if (mwrite(mfile, (char *)&midi_length, sizeof(midi_length)) !=
sizeof(midi_length)) {
sprintf(MidiError, "Couldn't write track length");
return (0);
}
if (mwrite(mfile, (char *)tchunk->event_start, tchunk->length)
!= tchunk->length) {
sprintf(MidiError, "Couldn't write track events");
return (0);
}
return (1);
}
int
merge_tracks(out, in, tscalar, num, delta)
TCHUNK *out;
TCHUNK **in;
int *tscalar;
int num;
int delta;
{
struct evnt {
long timing;
EVENT_TYPE type;
int size;
short remainder;
unsigned char event[256];
unsigned char run_state;
} *ev;
long timing;
int *atracks;
int endtime;
int i;
int j;
int foo;
int lasttime;
int next;
int num_active;
unsigned char event[256];
if ((ev = (struct evnt *)malloc(sizeof(struct evnt) * num))
== NULL) {
sprintf(MidiError, "Not enough memory for event buffers");
return (-1);
}
if ((atracks = (int *)malloc(sizeof(int) * num)) == NULL) {
sprintf(MidiError, "Not enought memory for atracks");
free((char *)ev);
return (-1);
}
endtime = delta;
/* get first events from each in track */
for (i = 0; i < num; i++) {
if ((ev[i].size = get_smf_event(in[i], ev[i].event,
&ev[i].type)) == -1) {
free((char *)ev);
free((char *)atracks);
return (-1);
}
ev[i].run_state = get_running_state(in[i]);
/* convert time */
timing = var2fix(ev[i].event, &foo);
ev[i].timing = timing / tscalar[i] + delta;
ev[i].remainder = timing % tscalar[i];
/* alway force inclusion of running status */
ev[i].size -= foo;
for (j = 0; j < ev[i].size; j++)
ev[i].event[j] = ev[i].event[foo + j];
if (ev[i].type == METAEOT) {
if (ev[i].timing > endtime)
endtime = ev[i].timing;
} else
atracks[i] = 1;
}
num_active = num;
lasttime = 0;
while (num_active) {
/* find first active track */
next = 0;
for (i = 0; i < num; i++)
if (atracks[i]) {
next = i;
break;
}
/* now compare to find active track with smallest timing */
for (; i < num; i++)
if (atracks[i])
if (ev[i].timing < ev[next].timing)
next = i;
foo = fix2var(ev[next].timing - lasttime, event);
/* stick in a running state if it is missing */
if (!(ev[next].event[0] & 0x80))
event[foo++] = ev[next].run_state;
for (i = 0; i < ev[next].size; i++)
event[foo + i] = ev[next].event[i];
/*
{
int x;
printf("wrote: ");
for (x = 0; x < ev[next].size + foo; x++)
printf("0x%02x ", event[x]);
printf("\n");
}
*/
/* change timing value of event */
if (!put_smf_event(out, event, ev[next].size + foo)) {
free((char *)ev);
free((char *)atracks);
return (-1);
}
lasttime = ev[next].timing;
/* get replacement event */
if ((ev[next].size = get_smf_event(in[next], ev[next].event,
&ev[next].type)) == -1) {
free((char *)ev);
free((char *)atracks);
return (-1);
}
/*
{
int x;
printf("read: ");
for (x = 0; x < ev[next].size; x++)
printf("0x%02x ", ev[next].event[x]);
printf("\n");
}
*/
ev[next].run_state = get_running_state(in[next]);
/* convert time */
timing = var2fix(ev[next].event, &foo) + ev[next].remainder;
ev[next].timing += timing / tscalar[next];
ev[next].remainder = timing % tscalar[next];
ev[next].size -= foo;
for (j = 0; j < ev[next].size; j++)
ev[next].event[j] = ev[next].event[foo + j];
if (ev[next].type == METAEOT) {
if (ev[next].timing > endtime)
endtime = ev[next].timing;
atracks[next] = 0;
num_active--;
}
}
free((char *)ev);
free((char *)atracks);
return (endtime - lasttime);
}
int
fix2var(val, ptr)
long val;
unsigned char *ptr;
{
int i;
unsigned char buf[4];
unsigned char *bptr;
buf[0] = buf[1] = buf[2] = buf[3] = 0;
bptr = buf;
*bptr++ = val & 0x7f;
while ((val >>= 7) > 0) {
*bptr |= 0x80;
*bptr++ += (val & 0x7f);
}
i = 0;
do {
*ptr++ = *--bptr;
i++;
} while (bptr != buf);
return (i);
}
long
var2fix(var, delta)
unsigned char *var;
int *delta;
{
long fix;
fix = 0;
*delta = 0;
if (*var & 0x80)
do {
fix = (fix << 7) + (*var & 0x7f);
(*delta)++;
} while (*var++ & 0x80);
else {
fix = *var++;
(*delta)++;
}
return (fix);
}
void
free_track(track)
TCHUNK *track;
{
if (track == NULL)
return;
if (track->event_start != NULL)
free(track->event_start);
track->event_start = NULL;
track->events = NULL;
track->length = 0;
track->pos = 0;
track->msize = 0;
track->read_rs = 0;
track->write_rs = 0;
}
void
init_track(track)
TCHUNK *track;
{
track->event_start = NULL;
track->events = NULL;
track->msize = 0;
track->length = 0;
track->pos = 0;
track->read_rs = 0;
track->write_rs = 0;
strncpy(track->str, "MTrk", 4);
}
unsigned char
get_running_state(track)
TCHUNK *track;
{
return (track->read_rs);
}
int
mread(dev, buf, size)
int dev;
char *buf;
int size;
{
int num_read;
int total_read;
total_read = 0;
do {
if ((num_read = read(dev, buf, size - total_read)) == -1) {
perror("");
return (-1);
}
if (num_read == 0)
break;
total_read += num_read;
buf += num_read;
} while (size > total_read);
if (total_read == 0)
MidiEof = 1;
return (total_read);
}
int
mwrite(dev, buf, size)
int dev;
char *buf;
int size;
{
int num_written;
int total_written;
total_written = 0;
do {
if ((num_written = write(dev, buf, size - total_written))
== -1) {
perror("");
return (-1);
}
if (num_written == 0)
break;
total_written += num_written;
buf += num_written;
} while (size > total_written);
return (total_written);
}